|
NESTE CAPÍTULO
Chegou a herança! Chegou a herança! Você está excitado? Talvez o mais requisitado recurso para o Visual Basic deste a versão 1.0 tem sido a herança. Finalmente, a herança chegou para satisfazer o desejo dos adeptos da programação orientada para objeto (POO). E ela venho bem. Muitos dos desenvolvedores podem desprezar este recurso, mas ele é realmente muito poderoso e, com o poder vem a responsabilidade. Você precisa ter plena consciência de quando está fazendo uso da herança. Se você nunca teve contato com o conceito de herança antes, você pode estar se perguntando o que vem a ser herança ou, ao menos, o que ela pode fazer por você. Este capítulo procurará apresentar-lhe a herança no estilo .NET. |
Por anos, um dos recursos mais requisitados para o VB tem sido a herança. A Microsoft tem disponibilizado uma forma de herança no VB: a herança de interface, disponível no VB desde que o VB iniciou o suporte à criação de objetos no VB4. Os desenvolvedores, contudo, queriam ter o que é conhecido como herança de implementação. Com o VB .NET, os desenvolvedores finalmente realizaram seu desejo, graças à CLR (common language runtime). Se você não sabe o que são herança de interface e herança de implementação, veja a seguir.
Herança é um modo poderoso de reutilizar código. Com a herança, você escreve uma classe que contém algumas propriedades e métodos. Esta classe torna-se a base para outras classes e é conhecida como classe base. Classes que usam esta classe base são conhecidas como classes derivadas. Muito frequentemente, as classes derivadas estendem a funcionalidade da classe base.
Imagine se você quisesse criar uma aplicação para modelar parte de uma universidade. Como parte da aplicação, você gostaria de criar dois objetos representando estudantes e professores. Estudantes e professores têm ambos um número de coisas em comum. Cada um tem um nome, um endereço, e assim por diante. Contudo, cada um também tem algumas diferenças. Estudantes têm especialização, enquanto professores têm cursos que ministram. Estudantes são cobrados, enquanto professores são pagos pelos seus trabalhos. Professores aprovam , enquanto estudantes são aprovados.
Como você pode ver, há similaridades e diferenças que devem ser levadas em conta quando estes objetos são criados. O objetivo de se usar herança é escrever o código para as similaridades apenas uma vez, e então, reutilizá-lo em ambos os objetos: estudantes e professores.
No VB6, você tinha o que era chamado de herança de interface. Com a herança de interface, você podia criar a interface de um componente reutilizável. Suponhamos que você queira criar um objeto Pessoa que tenha todas as propriedades compartilhadas por estudantes e professores. No caso do VB6, você criaria um módulo de classe que contivesse as definições daquelas propriedades, mas não o verdadeiro código de implementação. Sua classe de interface poderia ser chamada IPessoa e parecer-se com isto:
Public Property Get Nome() As String End Property Public Property Let Nome(psNome As String) End Property Public Property Get Endereco() As String End Property Public Property Let Endereco(psEndereco As String) End Property
Public Function Matricular() As Boolean
End Function
No VB6, você podia criar uma interface usando um módulo de classe. Tecnicamente, o VB não podia criar interfaces, mas você podia usar o truque de criar um módulo de classe apenas com definições de propriedades e métodos, e então implementá-los em outra classe. Era apenas a criação de uma classe, mas sem nenhum código de implementação dentro dela, assim ela era vista como uma interface. Por exemplo, o método Matriculado não tem nenhum código para tratar a matrícula. E, se você está curioso para saber o porquê de haver um método Matricular na interface IPessoa, é por que professores também podem fazer cursos na universidade, o que alguns fazem, porque eles têm a vantagem de fazê-lo de graça em muitas instituições.
Para usar esta interface em outras classes do VB6, você tinha de adicionar esta linha de código na classe:
Implements IPessoa
Esta linha prepara a sua nova classe para herdar a interface da classe IPessoa. No VB6, você podia realmente adicionar código de implementação dentro da classe de interface, mas ele era ignorado quando você implementava a interface na classe derivada.
Imagine que você tenha usado a palavra Implements em um classe chamada Estudante. Esta classe precisa agora implementar cada propriedade pública, método e evento encontrado em IPessoa. Portanto, o seu código para Estudante incluiria algo parecido com o seguinte:
Public Property Get IPessoa_Nome() As String
IPessoa_Nome = msNome
End Property
Public Property Let IPessoa_Name(psNome As String)
msName = psName
End Property
Public Property Get IPessoa_Endereco() As String
IPessoa_Endereco = msEndereco
End Property
Public Property Let IPessoa_Endereco(psEndereco As String)
msEndereco = psEndereco
End Property
Public Function IPessoa_Matricular() As Boolean
'Abre banco de dados
'Verifica se curso está completo
'Se não, adiciona estudante
End Function
O código em Estudante contém a implementação da interface projetada em IPessoa.
Examinando isto, você provavelmente reconhecerá que, na maior parte do tempo, a implementação para a classe Estudante será a mesma para a classe Professor, ao menos para estes membros de classe. Não seria mais fácil criar este código de implementação na classe IPessoa e então apenas ter este código trazido para dentro da classe Estudante e Professor? Isto é o que a herança de implementação faz.
No VB .NET, você não tem que criar uma interface em separado, embora você até possa. Você pode criar uma classe com código de implementação e herdá-la em outra classe. Isto significa que você poderia adicionar o código para as propriedades Nome e Endereco e para o método Matricular em uma classe Pessoa. Então, as classes Professor e Estudante poderiam ambas herdar da da classe base Pessoa. Estudante e Professor teriam ambos acesso ao código interno à Pessoa, ou poderiam substituí-lo pelo seu próprio código se fosse preciso.
Uma coisa a observar é que no VB .NET, todas as classes são herdáveis por default. Isto inclui formulários, que são apenas mais um tipo de classe. Isto significa que você pode criar um novo formulário baseado em um já existente. Você verá um exemplo de herança de formulários mais adiante neste capítulo.
Embora o VB .NET possibilite a verdadeira herança de implementação, as interfaces não desapareceram. Na verdade, elas foram muito melhoradas no VB .NET; juntas, herança e interfaces lhe dão a possibilidade de usar polimorfismo. Isto será examinado no final deste capítulo.
Crie uma nova aplicação Windows com o VB .NET e nomeie o projeto TesteDeHeranca. Adicione um botão ao formulário e vá para a janela de código. No código, adicione a seguinte classe. Certifique-se de que você a adiciona fora da definição de classe para o formulário!
Public Class Pessoa
Dim msNome, msEndereco As String
Property Nome() As String
Get
Nome = msNome
End Get
Set(ByVal Value As String)
msNome = Value
End Set
End Property
Property Endereco() As String
Get
Endereco = msEndereco
End Get
Set(ByVal Value As String)
msEndereco = Value
End Set
End Property
Public Function Matricular() As Boolean
'Verifica se há disponibilidade de aulas
'Se há, matricula pessoa
Matricular = True
End Function
End Class
Este código cria a classe Pessoa com duas propriedades, Nome e Endereco, e o método Matricular. Até aqui, não há nada sobre esta classe que você já não tenha visto antes.
Agora, adicione uma segunda classe chamada Estudante. O seu código deve ficar assim:
Public Class Estudante
Inherits Pessoa
End Class
Como você pode ver, não há nenhum código de implementação em Estudante. Não há nenhuma propriedade nem métodos. Tudo o que você faz é herdar da classe Pessoa.
Agora, no procedimento de evento Button1_Click no formulário, adicione o seguinte código:
Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Dim mEstudante As New Estudante() MsgBox(mEstudante.Matricular) End Sub
O seu formulário está criando uma instância da classe Estudante. O único código na classe Estudante é um comando Inherits que herda a classe Pessoa. Contudo, porque o VB .NET suporta herança de implementação, você poderá chamar o método Matricular da classe Pessoa de dentro da classe Estudante, mesmo que a classe Estudante não defina explicitamente um método Matricular. Porém, o método Matricular está presente porque Estudante herda o método de Pessoa. Vá em frente e rode o projeto para verificar que a caixa de mensagem retorna um valor True.
É possível instanciar Pessoa diretamente? Neste caso, sim. Por exemplo, você poderia modificar o procedimento de evento Button1_Click para ficar assim:
Dim mEstudante As New Estudante() Dim mPessoa As New Pessoa() MsgBox(mEstudante.Matricular) MsgBox(mPessoa.Matricular)
Ambas as chamadas ao método Matricular funcionarão.
Isto pode gerar uma série de perguntas. Por exemplo, poderia Estudante modificar o método Matricular de tal forma que ela não estaria usando o código em Pessoa? A resposta é sim. Pessoa poderia impedir que alguém a instanciasse diretamente e, com isto, forçar as pessoas a instanciar somente outras classes que herdam dela? Novamente, a resposta é sim. Você verá isto é muito mais na medida que você aprenda mais sobre o que pode ser feito com herança.
O VB .NET apresenta o conceito de membros compartilhados. Membros compartilhados é uma forma de criar um membro (propriedade, procedimento ou campo) que é compartilhado entre instâncias de uma classe. Por exemplo, você poderia criar uma propriedade para uma string de conexão a banco de dados. Ela seria a mesma para todas as instâncias, de modo que você poderia atribuí-la em uma instância e todas as demais instâncias poderiam ver seu valor. Propriedades compartilhadas são muito usadas em herança, de forma que os objetos criados de classes derivadas possam compartilhar o mesmo membro por todas as instâncias da classe. Contudo, membros compartilhados também podem ser usados independente mente de você estar usando herança.
Para ver como propriedades compartilhadas funcionam, suponha que você queira criar um mecanismo de log para gravar todo tipo de mensagem criada por uma aplicação (mensagens de informação, mensagens de erro e assim por diante). Você quer gravar a mensagem em um arquivo de log e manter a contagem do total de mensagens independentemente do tipo de mensagem.
Inicie o Visual Studio .NET e crie uma nova aplicação Windows e chame-a de TesteLogMensagem. Crie um novo projeto, mas mantenha-o na mesma solution. Este novo projeto deve ser uma biblioteca de classes e nomei-o LogDeMensagem.
No código para o LogDeMensagem, mude o nome da classe para Logger e entre com o código a seguir:
Imports System.IO
Public Class Logger
Public Function LogMsg(ByVal psMensagem As String)
Static iCount As Integer
iCount += 1
Dim file As TextWriter = New StreamWriter("c:\output.txt", True)
file.WriteLine(psMensagem & ". " & iCount)
file.Close()
End Function
End Class
Nesta aplicação, você está criando um método chamado LogMsg, que aceita uma string do cliente. O método tem um inteiro estático chamado iCount. iCount conta o número de mensagens que o logger grava e é adicionado a cada mensagem de modo a você poder ver seu valor.
Retorne à aplicação TesteLogMensagem. Dê um clique duplo no nó References da janela do Solution Explorer e escolha Add Reference. Vá à tab Project e você verá o projeto LodDeMensagem. Clique no botão Select e então em OK. Isto adiciona uma referência da aplicação Windows para a biblioteca de classes que você criou.
No formulário, adicione dois botões. Vá à janela de código e entre com o seguinte código (o código gerado pelo Windows Form Designer é ocultado):
Imports LogDeMensagem
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Dim infoLogger As New LogDeMensagem.Logger()
Dim errorLogger As New LogDeMensagem.Logger()
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
infoLogger.LogMsg("Mensagem de informação")
End Sub
Private Sub Button2_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Button2.Click
errorLogger.LogMsg("Mensagem de erro")
End Sub
End Class
Agora, rode a aplicação (certifique-se de que a aplicação selecionada para startup é TesteLogMensagem, que seguramente será se você a criou primeiro). Clique em cada um dos botões. Se você olhar no arquivo output.txt, verá algo assim:
Mensagem de informação. 1 Mensagem de erro. 1
Você notará que ambos os objetos criados (infoLogger e errorLoger) gravaram o arquivo de texto. Contudo, a contagem estática do número de mensagens mostra ambas as mensagens com o número um. Por que?
Você criou dois objetos separados na memória. Eles são completamente separados, de modo que cada objeto tem seu próprio método LogMsg, significando que cada um tem sua própria cópia da variável estática iCount. Portanto, uma mensagem enviada usando infoLogger obtém o valor 1, mas uma mensagem enviada com errorLogger vai ao outro objeto na memória e também obtém 1.
Para evitar esta situação, você poderia compartilhar o método LogMsg. Isto permite que você compartilhe o método entre múltiplas instâncias do objeto, significando dizer que todos os objetos irão chamar a mesma cópia do método.
Para compartilhar a função LogMsg, retorne à aplicação LogDeMensagem e , na classe Logger, modifique a definição da função como a seguir:
Public Shared Function LogMsg(ByVal psMensagem As String)
Adicionando a palavra Shared, este método irá agora ser compartilhado entre múltiplas instâncias da classe. Para testar isto, delete o arquivo output.txt. Agora, rode a aplicação novamente e pressione cada um dos botões uma vez. Se você examinar o arquivo output.txt novamente, você verá que a seguinte saída foi produzida:
Mensagem de informação. 1 Mensagem de erro. 2
Agora, você pode ver que, independente de qual objeto você usa, a contagem do número de mensagens é incrementada corretamente. Isto significa que o mesmo método LogMsg foi chamado de dois objetos diferentes, porque ele foi compartilhado entre os dois objetos.
Há várias palavras chave associadas com herança. Lembre-se que por default, toda classe que você criar é herdável. Você herda de classes usando a palavra Inherits. A classe de onde você herda é chamada de classe base. A palavra Inherits somente pode ser usada em classes e interfaces. É importante lembrar que uma classe derivada somente pode herdar de uma única classe base, ou seja, não há herança múltipla.
O modificador NotInheritable é usado para marcar uma classe como não herdável, em outras palavras, ela não pode ser usada como classe base. Se você fosse modificar a classe pessoa para não poder ser herdada, você codificaria assim:
Public NotInheritable Class Pessoa
Se você modificar a definição da classe Pessoa para ficar como na linha acima, Estudante não mais poderá herdar da classe Pessoa.
Em contraste com o modificador NotInheritable, há também o modificador MustInherit. MustInherit diz que uma classe não pode ser instanciada diretamente, mas que ela precisa ser herdada de uma classe derivada e a classe derivada pode, então, ser instanciada.
Se você mudar Pessoa para incluir a palavra MustInherit, ela ficará assim:
Public MustInherit Class Pessoa
Agora, você não poderá usar a seguinte linha de código no cliente:
Dim mPessoa As New Pessoa
Contudo, você ainda poderá herdar Pessoa na sua classe Estudante e o seu cliente poderá instanciar Estudante.
A sobreposição da herança de uma propriedade ou método é a substituição, em uma classe derivada, da implementação da propriedade ou do método existente na classe base por uma implementação criada na classe derivada. Quando o cliente chama o membro sobreposto, o código executado é aquele da classe derivada e não o da classe base.
Quando você herda uma classe base, o padrão é as propriedades e métodos não poderem ser sobrepostos. Dado o seu exemplo anterior em TesteDeHeranca, você não poderia ter criado uma função chamada Matricular em Estudante porque já existia uma em Pessoa.
Há um modificador, NotOverridable, que diz que uma propriedade ou método em particular não podem ser sobrepostos. A despeito de os métodos normalmente já não poderem ser sobrepostos, você pode usar esta palavra em um único caso: se você tiver um método que já está sobrepondo um método de classe base, você pode marcá-lo como NotOverridable.
Se você quiser que uma propriedade ou método possa ser sobreposto, você pode marcá-lo com o modificador Overridable conforme mostrado aqui:
Public Overridable Function Matricular() As Boolean
Agora, sua classe Estudante pode criar seu próprio método Matricular. Para fazer isto, o seu cliente terá que usar o modificador Overrides, de modo que ele ficará assim:
Public Overrides Function Matricular() As Boolean
O que acontece se o método Matricular for marcado como Overridable na classe base, mas você não o sobrepor na classe derivada? Neste caso, ele se comportará como qualquer outro método na classe base e as chamadas na classe derivada irão chamar a implementação do método na classe base.
No IDE do VB .NET, também é possível usar as combos no topo da janela de código para sobrepor métodos ou implementar interfaces. Como você pode ver na figura 5.1, há duas classes no arquivo. A classe Estudante está herdando da classe Pessoa e, assim fazendo, é capaz de sobrepor o método Matricular. Se você mudar a seleção da combo com o nome da classe para (Overrides), o método Matricular aparece na combo com os nomes dos métodos e pode ser usada para criar a versão de sobreposição do método na classe Estudante.

Há também um modificador MustOverride. Ele força uma classe derivada a sobrepor a propriedade ou método. O modificador MustOverride muda a estrutura da propriedade ou do método. Isto porque, se a propriedade ou método devem ser sobreposto, você não adiciona nenhum código de implementação nele. De fato, não há sequer um End Property ou End Sub ou End Function quando se usa o modificador MustOverride. Se uma classe tem ao menos um método ou propriedade marcada como MustOverride, esta classe precisa ser marcada como MustInherit, porque o método pode ser usado apenas se ele for sobreposto em uma outra classe. Portanto, a classe precisa ser herdada para ser usada.
Por exemplo, aqui você tem uma classe base Transporte, que tem o método Mover marcado como MustOverride. Isto significa que Transporte precisa ser marcada com MustInherit. A classe Trem herda da classe Transporte e então sobrepõe o método Mover.
Public MustInherit Class Transporte
MustOverride Function Mover() As Boolean
End Class
Public Class Trem
Inherits Transporte
Public Overrides Function Mover() As Boolean
'o código vai aqui
End Function
End Class
MyBase e MyClass
Embora você possa sobrepor uma propriedade ou método de uma classe base na sua classe derivada, você ainda pode acessar as propriedades e métodos da classe base usando a palavra MyBase. Isto permite que você chame os membros da classe base mesmo que você os tenha sobreposto na classe derivada.
Por exemplo, assuma que você quer ter uma classe Transporte com uma função sobreposta chamada Mover. A classe Trem herda da classe Transporte e implementa seu próprio método Mover sobrepondo aquele da classe Transporte. Agora, de dentro de Trem, você pode chamar o método Mover em Trem ou a versão da classe Transporte. Para chamar a versão Mover da classe base Transporte, você usa a sintaxe: MyBase.Mover. Veja abaixo como poderia ficar o código de definição das classes:
Public Class Transporte
Overridable Function Mover() As Boolean
MsgBox("Olá da classe Transporte!")
End Function
End Class
Public Class Trem
Inherits Transporte
Public Overrides Function Mover() As Boolean
MsgBox("Olá da classe Trem!")
End Function
Public Sub ChamarMetodos()
Mover()
MyBase.Mover()
End Sub
End Class
Relacionado a MyBase, existe a palavra MyClass. Assuma que, na sua classe base, você tem um método A chamando um método sobreposto B. Se você quiser garantir que está chamando o método B que você escreveu na classe base e não o método derivado e sobreposto na classe derivada, chame o método B com o modificador MyClass, assim: MyClass.B.
Por exemplo, assuma que você tem uma classe Transporte que tem dois métodos: FazerReserva e ComprarBilhete. FazerReserva chama ComprarBilhete. FazerReserva e ComprarBilhete são ambas marcadas com Overridable. Sua classe Trem pode herdar de Transporte e criar um método ComprarBilhete que sobrepõe ComprarBilhete na classe base Transporte. Se você não criar um FazerReserva em Trem, sua chamada a FazerReserva irá usar o código da classe Transporte. Contudo, se o código em Transporte.FazerReserva chama ComprarBilhete, por padrão você irá chamar o método ComprarBilhete que você criou em Trem. Eis o código:
Public Class Transporte
Overridable Function FazerReserva() As Boolean
ComprarBilhete()
'etc
End Function
Overridable Function ComprarBilhete() As Boolean
MsgBox("Implementação Genérica na classe Transporte")
End Function
End Class
Public Class Trem
Inherits Transporte
Public Overrides Function ComprarBilhete() As Boolean
MsgBox("Implementação específica de Trem")
End Function
End Class
Agora, suponha que você queira que FazerReserva chame o método ComprarBilhete em Transporte, mesmo se Trem sobrepõe ComprarBilhete. Para conseguir isto, basta mudar Transporte.FazerReserva para:
Public Class Transporte
Overridable Function FazerReserva() As Boolean
MyClass.ComprarBilhete()
'etc
End Function
É importante observar que, se a sua classe Trem sobrepuser FazerReserva, MyClass não funcionará. A razão é que a implementação de FazerReserva na classe base não será chamada (a menos que Trem chame MyBase.FazerReserva por alguma razão).
Compreendendo Acesso e Herança
O acesso pode ser um pouco complicado quando se usa herança. Há vários modificadores usados para configurar o acesso. Eles são:
Public— Acessível por todo o projeto e qualquer outro projeto que o referencie
Friend— Acessível dentro do projeto, mas não fora dele
Protected— Acessível dentro da classe na qual foi criado ou qualquer classe derivada da classe que contém o elemento protegido
Protected Friend— Acessível dentro do mesmo projeto, em uma classe derivada ou ambos
Private— Não acessível fora do módulo ou classe contendo o elemento
Para ver um exemplo disto, veja o código a seguir. Você tem três classes em uma biblioteca de classes: Pessoa, Estudante e Foo. Pessoa é um classe base contendo um Sub de cada tipo de acesso mencionado acima. Estudante é uma classe derivada de Pessoa. Foo é uma classe no mesmo projeto, mas não deriva de Pessoa.
A seguir, você tem uma aplicação cliente que instancia apenas a classe Estudante. Veja aqui o código para a biblioteca de classe:
Public Class Pessoa
Public Overridable Sub PublicSub()
End Sub
Friend Overridable Sub FriendSub()
End Sub
Protected Overridable Sub ProtectedSub()
End Sub
Protected Friend Overridable Sub ProtectedFriendSub()
End Sub
Private Sub PrivateSub()
End Sub
End Class
Public Class Estudante
Inherits Pessoa
Public Overrides Sub PublicSub()
End Sub
Friend Overrides Sub FriendSub()
End Sub
Protected Overrides Sub ProtectedSub()
End Sub
Protected Friend Overrides Sub ProtectedFriendSub()
End Sub
End Class
Public Class Foo
Dim mEstudante As New Estudante()
Public Sub Test()
mEstudante.PublicSub()
mEstudante.FriendSub()
mEstudante.ProtectedFriendSub()
End Sub
End Class
Note que Estudante, que é derivada de Pessoa, pode ver tudo menos o método PrivateSub de Pessoa, porque ele está marcado como Private e, portanto, é apenas visível para Pessoa.
Foo, por outro lado, pode ver apenas PublicSub, FriendSub e ProtectedFriendSub. ProtectedSub pode ser visto apenas na classe na qual ele existe e em classe derivada desta classe base.
Finalmente, veja o código do cliente que instancia um objeto Estudante:
Imports AccessibilityTest
Public Class Form1
Inherits System.Windows.Forms.Form
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim mEstudante As Estudante
mEstudante.PublicSub()
End Sub
End Class
Note que, no cliente, o único método que está acessível é o método PublicSub. A figura 5.4 mostra cada Sub e até onde ele é visível em cada caso.

Polimorfismo é a habilidade de mudar a implementação de uma classe base para diferentes objetos. Por exemplo, se você tem uma bicicleta e um carro, ambos podem se mover, mas eles o fazem de modos bem diferentes. Eles usam diferentes mecanismos para se movimentarem e a distância que cada um pode se mover em uma hora é significativamente diferente. Mesmo assim, tanto a classe Carro como a classe Bicicleta podem herdar da classe base Transporte, que poderia também ser usada como a base para uma classe Aeroplano, um classe Trem, e etc.
Polimorfismo era possível no VB6 usando interfaces, o que examinaremos em um momento. O VB .NET permite que você realize polimorfismo usando herança. A diferença do que você tem feito até aqui é que você pode realmente usar a classe base como um tipo de variável e você pode manipular qualquer classe derivada com aquela variável
Por exemplo, examine o código a seguir. A classe Transporte contém somente um método, Mover. Duas classes herdam de Transporte: Carro e Bicicleta. Ambas sobrepõem o método Mover. O código fica assim:
Public MustInherit Class Transporte
Public MustOverride Function Mover() As Boolean
End Class
Public Class Bicicleta
Inherits Transporte
Overrides Function Mover() As Boolean
'código vai aqui
Mover = True
End Function
End Class
Public Class Carro
Inherits Transporte
Overrides Function Mover() As Boolean
'código diferente vai aqui
Mover = True
End Function
End Class
Até aqui, parece igual. Contudo, note agora o que o seu cliente pode fazer. Ele não pode criar diretamente uma instância de Transporte, porque ela está marcada como MustInherit. Mas, você pode declarar uma variável do tipo Transporte. Você pode estar certo de que qualquer objeto que herde de Transporte tem um método Mover e você não tem que se preocupar sobre que tipo de objeto é. O código do seu cliente poderia ser como este:
Protected Sub Button1_Click _
(ByVal sender As Object, ByVal e As System.EventArgs)
Dim mCarro As New Carro()
Dim mBicicleta As New Bicicleta()
EntreEmMovimento(mCarro)
EntreEmMovimento(mBicicleta)
End Sub
Public Sub EntreEmMovimento(ByVal Veiculo As Transporte)
If Veiculo.Mover() Then
'faça algoEnd If End Sub
Você notou que o Sub EntreEmMovimento aceita um argumento do tipo Transporte.Este Sub não se preocupa se você lhe passa um objeto do tipo Carro ou Bicicleta. Por que o objeto sendo passado herda de Transporte, ele garantidamente suporta o método Mover, desta forma o código iria rodar sem problemas.
Interfaces ainda existem no VB .NET. No VB6 e no COM, elas eram muito usadas quando você necessitava de poder modificar o seu código sem travar clientes existentes. Você poderia modificar de fato a estrutura de suas classes, mas fornecendo uma interface que fosse igual à velha versão do componente. Isto manteria as aplicações clientes já existentes enquanto, ao mesmo tempo, você poderia modificar as classes para melhorar a funcionalidade.
No VB .NET, você ainda pode usar as interfaces desta forma. Contudo, as interfaces são melhor usadas quando você quer poder usar diferentes tipos de objetos de uma mesma forma, e os objetos não necessariamente compartilham o mesmo tipo básico. Por exemplo, a interface IEnumerable é usada para expor um enumerador para uma classe. Usando esta interface, você pode mover-se através de uma classe usando For Each, mesmo que a classe não seja baseada em um objeto Collection.
Você verá um exemplo de polimorfismo com interfaces usando o mesmo exemplo do Carro e da Bicicleta. Primeiro, Transporte será criada como uma interface e não uma classe base, o que faz mais sentido: o método Mover, é bem provável que seja completamente diferente entre Carro e Bicicleta. Então, você irá implementar a interface Transporte. Finalmente, você poderá chamar os objetos do cliente.
Interface Transporte
Function Mover() As Boolean
End Interface
Public Class Bicicleta
Implements Transporte
Function Mover() As Boolean Implements Transporte.Mover
'código vai aqui
Mover = True
End Function
End Class
Public Class Carro
Implements Transporte
Function Mover() As Boolean Implements Transporte.Mover
' código diferente vai aqui
Mover = True
End Function
End Class
Você notará que, agora, quando implementa de uma interface, você não tem que usar o modificador Overrides. Em adição, a definição do método é seguida de uma palavra Implements que especifica qual método na interface o método corrente está implementando. Isto lhe permite ter diferentes nomes para os métodos na classe que estiver implementando a interface. O código do cliente aqui será o mesmo de quando você usa polimorfismo de herança.
Você já leu um pouco sobre herança e, até aqui, tem sido sobre classes não-GUI. Contudo, formulários são classes, de forma que você pode herdar de formulários tão fácil como pode de qualquer outra classe. Desenvolvedores em VB têm solicitado a herança de formulários por muitos anos, e agora eles têm o que queriam, graças ao Framework .NET.
É fácil ver a herança em funcionamento em formulários, e há uns poucos detalhes que devem ser observados. Você irá iniciar com um exemplo simples e então estendê-lo à medida que trabalhar nele.
Inicie uma nova aplicação Windows no Visual Studio e nomeie-a baseVisual. Na realidade, você poderia criar como um projeto de tipo diferente, mas iniciar com uma aplicação Windows é quase sempre a forma mais fácil de começar.
O seu projeto baseVisual iniciará com um formulário na janela de designer. Para manter as coisas simples, adicione dois botões ao formulário. Para simplicidade do demo, deixe as coisas com o nome que tiverem. Mude a propriedade Text do primeiro botão para "Run Code" (executar código) e a propriedade Text do segundo botão para Close. Mova os botões para o lado direito do formulário, como mostrado na figura 5.5.

Vá para a janela de código e, para o Button1, adicione o seguinte código:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
MsgBox("Este é o código no formulário base")
End Sub
Agora, adicione o seguinte código para o Button2:
Private Sub Button2_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button2.Click Me.Close() End SubNeste ponto, todo o código está dentro de uma classe pública chamada Form1. Você pode ser tentado a criar um novo formulário no projeto que herda de Form1, mas a herança de formulários funciona um pouco diferente. A primeira coisa que você precisa fazer de diferente é mudar o tipo de projeto.
Clique com o botão direito no projeto baseVisual na janela do Solution Explorer e escolha Properties. Mude o Output Type para Class Library, como mostra a figura 5.6.

O projeto baseVisual compilará agora como uma DLL, mas ele tem o seu formulário dentro de si.
Adicione um novo projeto à solução. Faça do projeto uma aplicação Windows e nomeie-o derivedVisual. O seu novo projeto derivedVisual automaticamente virá com um novo formulário chamado Form1. Delete Form1 do derivedVisual.
Você pode estar tentado a herdar do formulário base de baseVisual agora, mas há um passo adicional: Você não pode derivar o formulário de um outro projeto até que o assembly seja criado. Portanto, clique com o botão direito no baseVisual no Solution Explorer e escolha Build. Note que você poderia derivar um novo formulário no mesmo projeto, baseado em Form1, sem ter que primeiro criar o assembly.
Após criar o assembly, clique com o botão direito novamente no projeto derivedVisual no Solution Explorer. Escolha Add e note que uma das escolhas no menu é Add Inherited Form (adicionar formulário herdado). Escolha para adicionar um formulário herdado e uma caixa de diálogo aparece. Ela irá perguntar-lhe pelo nome do formulário que você quer criar. O nome não importa, de modo que você pode deixá-lo como Form2.VB. Clique no botão Open.
Agora, uma nova caixa de diálogo aparece, que você pode ver na figura 5.7. Este é o Inheritance Picker (selecionador de herança), e permitirá que você localize um assembly compilado do qual herdar. O Inheritance Picker já mostra Form1 em baseVisual, porque ele está na mesma solution. Clique no Form1 em baseVisual para habilitar o botão OK e clique em OK.

Após clicar em OK, duas coisas acontecem. Primeiro, baseVisual aparece sob o nó References para o projeto. Mais notavelmente, um novo formulário é criado no derivedVisual. Surpresa! Ele parece-se exatamente como o formulário que você criou no baseVisual exceto por aqueles símbolos divertidos nos dois botões. Aqueles símbolos engraçados, mostrados na figura 5.8, indicam que os botões são herdados do formulário base.

Adicione um novo botão ao formulário. Note que ele não tem o símbolo indicativo de herança, porque, naturalmente, ele não é herdado. Configure derivedVisual como o projeto Startup e vá às propriedades do projeto. Ajuste Form2 para ser o Startup Object e rode a aplicação. Clicando no botão que você adicionou nada faz, porque você não adicionou nenhum código a ele. Contudo, clicando no botão Run Code faz com que seja exibida uma caixa de mensagem indicando que o código da classe base foi herdado. O botão Close fecha o formulário e encerra a aplicação.
Após a aplicação encerrar, dê um duplo clique no botão Close. Nada acontece. Isto porque, por default, você não pode mudar as propriedades de objetos em um formulário herdado ou escrever código para ele. Você pode, naturalmente, escrever código para o botão que você adicionou, mas não para os botões fornecidos pelo formulário de baseVisual.
Contudo, você pode mudar este comportamento. Volte para o formulário Form1 no projeto baseVisual. Clique no botão Close e note que a propriedade Modifiers está configurada para Friend. Mude Modifiers para Protected. Protected, que você viu antes, significa a classe pode ser modificada por classes herdeiras dela. Friend, o default, é visível a todas as classes no assembly em que ela reside. Contudo, o derivedVisual é um projeto separado e, portanto, um assembly separado. Agora, também mude a propriedade Modifiers do botão Run Code para Protected.
Recrie o assembly para o projeto baseVisual, porque você fez algumas mudanças no formulário. Retorne ao Form1 no derivedVisual. Você pode ter que fechar o designer de formulário e reabrí-lo para que ele exiba as mudanças que você fez na classe base. Dê um duplo clique no botão Run Code; desta vez, você é conduzido para a janela de código. Adicione o seguinte código:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
MsgBox("Este é o código no formulário derivado")
End Sub
Agora, rode o projeto novamente e clique no botão Run Code. Algo muito estranho ocorre: você recebe duas caixas de mensagem. A primeira é do formulário base, enquanto a segunda é do formulário derivado. Se você está curioso sobre o que está acontecendo, entenda que com formulários, você não está sobrepondo a funcionalidade da classe base; você a está estendendo em um sentido pseudo-polimórfico. Por enquanto, siga esta regra simples: se você quer sobrepor a funcionalidade de um controle no formulário base, não adicione nenhuma implementação para ele no formulário base.
Você tem ouvido um bocado sobre a herança entre linguagens na .NET, de modo que agora você poderá vê-la por si mesmo. Adicione um novo projeto à solution atual, mas, desta vez, crie uma aplicação Window C#. Noeie-a derivedVisualCs.
O seu novo projeto, derivedVisualCs, irá se iniciar com formulário, Form1.cs. Delete este formulário do projeto. Agora, adicione um formulário herdado a derivedVisualCs. Não se preocupe com o nome, mas uma vez mais escolha o Form1 do projeto baseVisual no selecionador de herança.
Adicione um novo botão ao formulário. Você pode dar um duplo clique ao botão e adicionar algum código. Contudo, você também terá que adicionar algum código extra porque o formulário não tem o código necessário para o C# abrí-lo como o objeto de inicialização (startup). Portanto, certifique-se de adicionar todo este código:
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void button3_Click(object sender, System.EventArgs e)
{
Windows.Forms.MessageBox("Olá do C#!");
}
Torne o projeto derivedVisualCs o seu projeto de inicialização e então rode a aplicação. Quando o formulário carregar, clique no botão button3 que você adicionou. Você deverá ver a caixa de mensagem que diz "Este é o código no formulário base", que você sabe foi escrito em VB .NET! Como você pode ver na Figura 5.9, você tem o código VB .NET rodando em uma aplicação C#. Você pode ver agora um pouco do poder da herança entre linguagens da plataforma .NET. Seus programadores de interface gráfica poderiam criar formulários básicos em uma linguagem e os desenvolvedores em outras linguagens poderiam herdar destes formulários e estendê-los como bem quisessem.

Quando a herança foi apresentada pela primeira vez em algumas outras linguagens, ela causou problemas porque os usuários foram longe demais, criando profundas e complexas ramificações de herança. O herança é poderosa e útil, mas não se deve abusar. Eu me lembro de ter trabalhado com um desenvolvedor que passou todo um dia projetando objetos e finalmente disse, "Eu não consigo entender onde usar polimorfismo.". Minha sugestão foi de que se ele não conseguia saber onde usá-lo, era porque, provavelmente, ele não necessitava fazê-lo.
Há, na verdade, três formas de construir um modelo de objetos:
Encapsulamento - Se você quer usar a funcionalidade de um objeto em outro, mas não tem neste outro nada que se assemelhe ao primeiro ou que sobrepõe a sua funcionalidade, não herde; ao invés disto, encapsule uma instância do objeto. Exemplo: você tem uma aplicação calculadora, e você quer exibir os números em vermelho quando são negativos. Você poderia definir um outro tipo de controle usando o controle TextBox ou Label que mostrariam o número em vermelho quando fosse negativo, ou você poderia simplesmente tratar o evendo Change do TextBox e ajustar a cor ali.
Implementação de Interface — Se os objetos derivados não compartilham muito em termos de implementação, use uma interface de uma classe base comum. Não faz sentido ter uma classe base para a qual todos os métodos devem ser sobrepostos; uma interface será, provavelmente, uma solução melhor. Classes também podem implementar múltiplas interfaces, de modo que um carro pode implementar tanto iMovidoAGas e iDirigivel, enquanto um aeroplano pode implementar iMovidoAGas e iVoador. Um pássaro, por outro lado, implementaria iComeVermes e iVoador, mas não implementaria iMovidoAGas ou iDirigivel.
Herança — Use herança se você quer utilizar a funcionalidade da classe base e estendê-la enquanto buscar imitar a classe base. Se alguns dos métodos na classe base não forem apropriados para a sua classe derivada, é um bom indicador de que a herança não foi a escolha correta. Em alguns casos, meramente encapsulando uma outra instância de classe e intermediando as chamadas para ela pode ser uma solução mais simples.
Como você pode ver, iniciar a usar herança não é difícil. Contudo, o desafio vem quando você projeta seus componentes e seus modelo de objetos. Herança é muito poderosa, mas você deve definir inteligentemente suas classes base para que possam ser úteis o bastante para tornarem-se herdáveis, mas genéricas o bastante para serem utilizáveis por muitas classes derivadas.
A herança está disponível entre linguagens .NET, como você viu na seção de herança visual. A herança visual é uma técnica poderosa para obter um conjunto comum de funcionalidade entre todos os formulários da sua aplicação. Foram-se os dias de ter que simular a herança de formulários criando novos módulos de classe para controlar a aparência dos formulários e usar matrizes de controles para criar novos controles programaticamente. Embora você ainda possa usar esta abordagem, as coisas tornaram-se mais simples porque você pode realmente herdar formulários e estendê-los através do designer de formulários.
Com a apresentação da herança de implementação, o VB .NET entra no reino das linguagens orientadas a objeto pela primeira vez. O VB6 tinha alguns dos recursos da OO, mas a adição da herança de implementação, um dos mais básicos conceitos de OO, em muito aumenta o prestígio da OO do VB .NET.